package com.such.kit.io.source;

import java.util.List;
import java.util.Map;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.such.kit.Callable;
import com.such.kit.datastructure.tree.bean.Node;
import com.such.kit.datastructure.tree.bean.Tree;
import com.such.kit.easyparam.ParamMap;
import com.such.kit.io.support.IOSource;
import com.such.kit.placeholder.PlaceholderCommons;
import com.such.kit.validate.ValidateCommons;

/**
 * @author SUCH
 * <pre>
 * 参考 {@link MemoryIOSource}
 * 
 * 与 MemoryIOSource 最大的差异在对数据的要求
 * MemoryIOSource 需要平面化的数据
 * MemoryMapIOSource 需要结构化（Map）的数据
 * 由于数据为结构化数据，MemoryMapIOSource 不建议构建动态 key
 * 
 * 数据结构
 * {rooKey: [{ ... }]}
 * </pre>
 */
public class MemoryMapIOSource extends IOSource {

	/** rooKey：根键 */
	private String rooKey;
	/** inputResult：[导入时] 存放数据 */
	private JSONObject inputResult = new JSONObject();
	private Tree<Map<String, Object>> inputTree = new Tree<Map<String, Object>>();
	/** outputResult：[导出时] 存放数据 */
	private JSONObject outputResult = new JSONObject();

	public MemoryMapIOSource(String rooKey) {
		this.rooKey = rooKey;
	}

	/**
	 * <pre>
	 * 获取根键
	 * </pre>
	 * @return 根键
	 */
	public String getRootKey() {
		return this.rooKey;
	}

	/**
	 * <pre>
	 * 获取 [导入时] 数据
	 * </pre>
	 * @return [导入时] 数据
	 */
	public Map<String, Object> getInputResult() {
		if (this.inputResult.size() == 0) {
			List<Node<Map<String, Object>>> roots = this.inputTree.getRoots();
			this.inputResult = buildInputResult(roots);
		}
		return this.inputResult;
	}

	private JSONObject buildInputResult(List<Node<Map<String, Object>>> nodes) {
		JSONObject inputResult = new JSONObject();
		if (ValidateCommons.isNotEmpty(nodes)) {
			for (Node<Map<String, Object>> node : nodes) {
				String childKey = node.getTitle();
				Map<String, Object> childData = node.getNodeObject();
				JSONArray childResults = inputResult.getJSONArray(childKey);
				if (ValidateCommons.isEmpty(childResults)) {
					childResults = new JSONArray();
				}
				JSONObject childResult = buildInputResult(node.getChildren());
				childResult.putAll(childData);
				childResults.add(childResult);
				inputResult.put(childKey, childResults);
			}
		}
		return inputResult;
	}

	/**
	 * <pre>
	 * 清空 [导入时] 数据
	 * </pre>
	 */
	public void clearInput() {
		this.inputResult = new JSONObject();
	}

	/**
	 * <pre>
	 * 以参数集构建动态 key，并放入其下属数据
	 * </pre>
	 * @param result 下属数据
	 */
	public void setOutputResult(Map<String, Object> result) {
		this.outputResult = new JSONObject(result);
	}

	/**
	 * <pre>
	 * 清空 [导出时] 数据
	 * </pre>
	 */
	public void clearOutput() {
		this.outputResult.clear();
	}

	private Callable<Map<String, Object>, Void> outputConvert;
	public void setOutputConvert(Callable<Map<String, Object>, Void> outputConvert) {
		this.outputConvert = outputConvert;
	}

	@Override
	public void doInput(String key, Map<String, Object> param, boolean lastBatch, Map<String, Object> extraParams) {
		if (ValidateCommons.isNotEmpty(key) && ValidateCommons.isNotEmpty(param)) {
			autoReplace(param);
			String inputKey = PlaceholderCommons.replacePlaceholder(key, param);
			String id = ParamMap.getValue("id", extraParams);
			String parentId = ParamMap.getValue("parentId", extraParams);
			Node<Map<String, Object>> node = new Node<Map<String, Object>>(id, parentId, inputKey);
			node.setNodeObject(param);
			this.inputTree.add(node);
		}
	}

	private JSONObject subOutputResult = null;
	@Override
	public void doOutput(String key, Map<String, Object> param, Callable<Map<String, Object>, Boolean> callable) throws Exception {
		if (ValidateCommons.isNotEmpty(key)) {
			String outputKey = key;
			if (this.rooKey.equals(outputKey)) {
				this.subOutputResult = this.outputResult;
			} else {
				outputKey = PlaceholderCommons.replacePlaceholder(key, param);
			}
			Object subObject = this.subOutputResult.get(outputKey);
			if (ValidateCommons.isNotEmpty(subObject)) {
				JSONArray subResults = null;
				if (subObject instanceof JSONObject) {
					subResults = new JSONArray();
					subResults.add(this.subOutputResult.getJSONObject(outputKey));
				} else if (subObject instanceof JSONArray) {
					subResults = this.subOutputResult.getJSONArray(outputKey);
				}
				for (int i = 0; i < subResults.size(); i++) {
					JSONObject subResult = subResults.getJSONObject(i);
					if (ValidateCommons.isNotEmpty(this.outputConvert)) {
						this.outputConvert.call(subResult);
					}
					this.subOutputResult = subResult;
					subResult.putAll(param);
					autoReplace(subResult);
					callable.call(subResult);
				}
			}
		}
	}

}
